package cn.fz.securityquickstart.config;

import cn.fz.securityquickstart.service.impl.DBUserDetailsManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

/**
 * 参考地址:
 * https://juejin.cn/column/7354960709011488805
 */
@Configuration //标明这个类为配置类，spring应用程序一启动，类中的been 就会被初始化在spring容器中
@EnableWebSecurity //开启spring security 自定义配置
public class WebSecurityConfig {
    /**
     * 通过下面配置类，在程序启动的时候。就会将用户名：user 密码：123456 角色：user这样一个用户存入内存中。
     * @return
     */
    // @Bean
    // public UserDetailsService userDetailsService() {
    //     // 1、创建基于内存的用户管理器
    //     InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
    //     // 3、将第二步创建的UserDetail对象交给UserDetailsManager 管理
    //     inMemoryUserDetailsManager.createUser(
    //         // 2、创建UserDetail 对象，用于管理用户名、用户密码、用户角色、用户权限
    //         // 下面代码创建了一个用户名为user,密码为123456，角色为user的用户对象
    //             User.withDefaultPasswordEncoder()
    //                     .username("user")
    //                     .password("123456")
    //                     .roles("user")
    //                     .build()
    //     );
    //     return inMemoryUserDetailsManager;
    // }

    @Bean
    public UserDetailsService userDetailsService(){
        // 创建基于数据库的用户管理器
        return new DBUserDetailsManager();
    }

    /**
     * 链接：https://juejin.cn/post/7506341622730604582
     * 在Spring Security中默认使用的PasswordEncoder要求数据库中的密码格式为：{id}password 。它会根据id去判断密码的 加密方式 ,
     * 所以我们之前数据库中存储的密码为{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW
     * 但是我们一般不会采用这种方式。所以就需要替换PasswordEncoder默认实现方式。 我们一般使用SpringSecurity为我们提供的BCryptPasswordEncoder。
     * 怎么替换呢？其实非常简单
     * 我们只需要使用把BCryptPasswordEncoder对象注入Spring容器中，SpringSecurity就会使用该PasswordEncoder来进行密码校验。
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 关闭csrf攻击防御
        // http.csrf(csrf->csrf.disable());
        // 下面这个等同于上面的csrf().disable()
        http.csrf(AbstractHttpConfigurer::disable);
        http.authorizeHttpRequests(authorize->authorize
            // .requestMatchers("/user/addUser").permitAll()

            // 因为下面两个的配置会对所有请求进行授权保护
            // 对所有请求开启授权保护
            .anyRequest()
            // 已认证的请求会自动授权
            .authenticated()
        );

        /**
         * 这个过滤器默认会这样处理：如果应用程序没有进行授权的话，它会将浏览器跳转到/login 这个地址当中，如果/login这个地址作为请求发送到后端服务器。
         *
         * SpringSecurity 就回去找默认的登录页面。
         *
         * 所以，我们要做的就是修改这个默认配置
         */
        // 默认表单授权方式
        http.formLogin(withDefaults());

        // 自定义登录页面
        http.formLogin(form->form.loginPage("/mylogin").permitAll());
        return http.build();
    }
}
